package utils.global.gray;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

@Aspect
public class GrayAop implements CommandLineRunner {

    @Autowired
    GrayProperties grayProperties;

    private ConcurrentHashMap<String, GrayRoule> roules = new ConcurrentHashMap<>();

    @Pointcut("@annotation(gray)")
    public void grayMethod(Gray gray) {
    }

    @Around(value = "grayMethod(gray)", argNames = "joinPoint,gray")
    public Object grayAop(ProceedingJoinPoint joinPoint, Gray gray) throws Throwable {

        /* 获取请求参数 */
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        /* 获取灰度策略名称 */
        String name = gray.name();

        /* 获取灰度策略版本 */
        String passVersion = roules.get(name).passVersion(request);
        Object proceed = null;

        /*  执行请求方法 */
        if (gray.version().equals(passVersion)) {
            /* 执行原方法 */
            proceed = joinPoint.proceed();
        } else {
            Method[] methods = joinPoint.getTarget().getClass().getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Gray.class)) {
                    Gray annotation = method.getAnnotation(Gray.class);
                    if (annotation.version().equals(passVersion) && annotation.name().equals(gray.name())) {
                        /* 执行策略版本方法 */
                        proceed = method.invoke(joinPoint.getTarget(), joinPoint.getArgs());
                    }
                }
            }
        }
        return proceed != null ? proceed : joinPoint.proceed();
    }

    @Override
    public void run(String... args) throws Exception {

        /* 获取灰度策略规则包路径 */
        String basePackage = grayProperties.getBasePackage();
        File file = new File(GrayAop.class.getClassLoader().getResource(basePackage.replace(".",
                "/")).getFile());
        File[] files = file.listFiles();

        if (files == null || files.length == 0) {
            throw new GrayRouleException();
        }

        /* 遍历灰度规则 */
        for (File classFile : files) {

            String absolutePath = classFile.getAbsolutePath();
            String substring = absolutePath.substring(absolutePath.indexOf("utils\\global"), absolutePath.indexOf(".class"));
            String ruleName = substring.replace(File.separator, ".");
            Class<?> clazz = Class.forName(ruleName);

            /* 判断是否为灰度规则类 */
            if (clazz.isAnnotationPresent(GraySetting.class)) {
                GraySetting annotation = clazz.getAnnotation(GraySetting.class);
                roules.put(annotation.name(), (GrayRoule) clazz.newInstance());
            }

        }
    }
}
